#include "QSPrecompiledHeaders.h"
#include "QSEngineImpl.h"
#include "QSSystemFactory.h"

using namespace QS;


#define CREATE_SYSTEM(sys, sysType) \
	if (!mFactory) \
		return 0; \
	if (!sys) \
		sys = mFactory->create##sysType(this); \
	return sys;


EngineImpl::EngineImpl()  
	: mFactory(0), mGui(0), mGame(0), mScript(0), mInput(0), mRender(0), mHost(0), mEvent(0),
	  mDone(false)
{
	mFactory = new SystemFactory();
}

EngineImpl::~EngineImpl()
{
	delete mScript;
	delete mGui;
	delete mInput;
	delete mRender;
	delete mEvent;
	delete mHost;

	delete mFactory;
}

GuiSystem* EngineImpl::getGuiSystem() { CREATE_SYSTEM(mGui, GuiSystem) }
GameSystem* EngineImpl::getGameSystem() { CREATE_SYSTEM(mGame, GameSystem) }
ScriptSystem* EngineImpl::getScriptSystem() { CREATE_SYSTEM(mScript, ScriptSystem) }
InputSystem* EngineImpl::getInputSystem() { CREATE_SYSTEM(mInput, InputSystem) }
RenderSystem* EngineImpl::getRenderSystem() { CREATE_SYSTEM(mRender, RenderSystem) }
EventSystem* EngineImpl::getEventSystem() { CREATE_SYSTEM(mEvent, EventSystem) }
HostSystem* EngineImpl::getHostSystem() { CREATE_SYSTEM(mHost, HostSystem) }

void EngineImpl::initSystems()
{
	//We have to be very careful about the order of initialization here
	getEventSystem()->initialize();
	getHostSystem()->initialize();
	getGameSystem()->initialize();
	getRenderSystem()->initialize();
	getInputSystem()->initialize();
	getGuiSystem()->initialize();
	getScriptSystem()->initialize();

	getEventSystem()->addEventListener(this);
}

void EngineImpl::updateSystems()
{
	getEventSystem()->update();
	getInputSystem()->update();
	getGuiSystem()->update();
	getRenderSystem()->update();
}


void EngineImpl::shutdownSystems()
{
	getEventSystem()->removeEventListener(this);

	//we must shut down the systems in the opposite order of creation
	getScriptSystem()->shutdown();
	getGuiSystem()->shutdown();
	getInputSystem()->shutdown();
	getRenderSystem()->shutdown();
	getGameSystem()->shutdown();
	getHostSystem()->shutdown();	
	getEventSystem()->shutdown();	
}

void EngineImpl::processEvent(const Event& evt)
{
	if (evt.getTypeId() == EventType::Quit)
		mDone = true;
}


void EngineImpl::start()
{
	try {
		initSystems();
		
		//main loop
		while(!mDone && !mRender->isRenderWindowClosed())
		{
			updateSystems();
		}

		shutdownSystems();
	}
	catch (QS::Exception& e)
	{
		getHostSystem()->showErrorDialog(e.what());
	}
	catch (std::exception& e)
	{
		getHostSystem()->showErrorDialog(e.what());
	}
	catch (...)
	{
		getHostSystem()->showErrorDialog("Unhandled exception occurred");
	}
}
